package com.shoping.common.advice;

import com.shoping.common.exception.*;
import com.shoping.common.response.ResponseData;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MultipartException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

/**
 * Description
 * 异常拦截器
 * Date: 2017/9/07
 *
 * @author: 曾维龙
 */
@Slf4j
public abstract class AbstractExceptionHandler {

    @ExceptionHandler(BusinessException.class)
    public ResponseData handleBusinessException(BusinessException e) {
        String message = String.format("业务范围内异常code=%s, message=%s", e.getCode(), e.getErrMsg());
        sendOnOffMessage(message, e);
        return new ResponseData<>(false, e.getCode(), e.getErrMsg());
    }

    @ExceptionHandler(ParamException.class)
    public ResponseData handleParamException(HttpServletRequest request, ParamException e) {
        String url = String.valueOf(request.getRequestURL());
        String message = String.format("参数异常url=%s, code=%s, message=%s", url, e.getCode(), e.getErrMsg());
        sendOnOffMessage(message);
        return new ResponseData<>(false, e.getCode(), e.getErrMsg());
    }

    @ExceptionHandler(AuthorizedException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public ResponseData handleAuthorizedException(HttpServletRequest request, AuthorizedException e) {
        String url = String.valueOf(request.getRequestURL());
        String loggerMessage = String.format("权限异常url=%s, code=%s, message=%s", url, e.getCode(), e.getErrMsg());
        sendOnOffMessage(loggerMessage);
        return new ResponseData<>(false, e.getCode(), e.getErrMsg());
    }

    @ExceptionHandler(HttpException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResponseData handleHttpException(HttpException e) {
        String loggerMessage = String.format("内部服务调用异常code=%s, message=%s", e.getCode(), e.getErrMsg());
        e.setErrMsg("Internal service call exception.");
        if (e.getReadOnly()) {
            sendErrorMsg(loggerMessage);
            return new ResponseData<>(false, e.getCode(), e.getErrMsg());
        }
        sendErrorMsg(loggerMessage, e);
        return new ResponseData<>(false, e.getCode(), e.getErrMsg());
    }

    @ExceptionHandler(ServiceException.class)
    @ResponseStatus(HttpStatus.EXPECTATION_FAILED)
    public ResponseData handleServiceException(ServiceException e) {
        String code = "10003";
        if (code.equals(e.getCode())) {
            log.info("文件损坏异常{}", e.getErrMsg());
            return new ResponseData<>(false, e.getCode(), e.getErrMsg());
        }
        String message = String.format("内部服务处理异常code=%s, message=%s", e.getCode(), e.getErrMsg());
        sendErrorMsg(message, e);
        e.setErrMsg("Internal service exception.");
        return new ResponseData<>(false, e.getCode(), e.getErrMsg());
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseData handleMissingServletRequestParameterException(HttpServletRequest request,
                                                                      MissingServletRequestParameterException e) {
        String url = String.valueOf(request.getRequestURL());
        String loggerMessage = String.format("请求参数错误url=%s, exMsg=%s", url, e.getMessage());
        sendOnOffMessage(loggerMessage);
        ParamException pe = new ParamException(e.getMessage());
        return new ResponseData<>(false, pe.getCode(), pe.getErrMsg());
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseData handleHttpRequestMethodNotSupportedException(HttpServletRequest request,
                                                                     HttpRequestMethodNotSupportedException e) {
        String url = String.valueOf(request.getRequestURL());
        String loggerMessage = String.format("请求方式异常url=%s, exMsg=%s", url, e.getMessage());
        sendOnOffMessage(loggerMessage);
        ParamException pe = new ParamException(e.getMessage());
        return new ResponseData<>(false, pe.getCode(), pe.getErrMsg());
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseData handleHttpMessageNotReadableException(HttpServletRequest request,
                                                              HttpMessageNotReadableException e) {
        String url = String.valueOf(request.getRequestURL());
        String loggerMessage = String.format("请求参数未进行Json序列化url=%s, exMsg=%s", url, e.getMessage());
        sendOnOffMessage(loggerMessage);
        String defaultMessage = e.getRootCause().getMessage();
        if (StringUtils.isBlank(defaultMessage)) {
            defaultMessage = "Request parameter JSON deserialization failed.";
        }
        ParamException pe = new ParamException(defaultMessage);
        return new ResponseData<>(false, pe.getCode(), pe.getErrMsg());
    }

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseData handleMethodArgumentTypeMismatchException(HttpServletRequest request,
                                                                  MethodArgumentTypeMismatchException e) {
        String url = String.valueOf(request.getRequestURL());
        String loggerMessage = String.format("请求方法参数值不匹配url=%s, exMsg=%s", url, e.getMessage());
        sendOnOffMessage(loggerMessage);
        ParamException pe = new ParamException("Request param type is mismatch");
        return new ResponseData<>(false, pe.getCode(), pe.getErrMsg());
    }

    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseData handleHttpMediaTypeNotSupportedException(HttpServletRequest request,
                                                                 HttpMediaTypeNotSupportedException e) {
        String url = String.valueOf(request.getRequestURL());
        String loggerMessage = String.format("不支持的请求体类型url=%s, exMsg=%s", url, e.getMessage());
        sendOnOffMessage(loggerMessage);
        ParamException pe = new ParamException("Request body type not supported");
        return new ResponseData<>(false, pe.getCode(), pe.getErrMsg());
    }

    /**
     * 处理请求参数格式错误 @RequestBody上validate失败后抛出的异常是MethodArgumentNotValidException异常。
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseData handleMethodArgumentNotValidException(HttpServletRequest request,
                                                              MethodArgumentNotValidException e) {
        String url = String.valueOf(request.getRequestURL());
        String loggerMessage = String.format("请求方法参数校验不通过url=%s, exMsg=%s", url, e.getMessage());
        sendOnOffMessage(loggerMessage);

        String defaultMessage = "Method argument not valid exception";
        String paramMessage = e.getBindingResult().getAllErrors()
                .stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
                .findFirst().orElse(defaultMessage);
        ParamException pe = new ParamException(paramMessage);
        return new ResponseData<>(false, pe.getCode(), pe.getErrMsg());
    }

    /**
     * 处理请求参数格式错误 @RequestParam上validate失败后抛出的异常是javax.validation.ConstraintViolationException
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseData constraintViolationExceptionHandler(HttpServletRequest request,
                                                            ConstraintViolationException e) {
        String url = String.valueOf(request.getRequestURL());
        String loggerMessage = String.format("请求方法参数校验不通过url=%s, exMsg=%s", url, e.getMessage());
        sendOnOffMessage(loggerMessage);

        String defaultMessage = "Method argument not valid exception";
        String paramMessage = e.getConstraintViolations()
                .stream().map(ConstraintViolation::getMessage)
                .findFirst().orElse(defaultMessage);
        ParamException pe = new ParamException(paramMessage);
        return new ResponseData<>(false, pe.getCode(), pe.getErrMsg());
    }

    /**
     * 处理Get请求中 使用@Valid 验证路径中请求实体校验失败后抛出的异常
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseData handleBindException(HttpServletRequest request, BindException e) {
        String url = String.valueOf(request.getRequestURL());
        String loggerMessage = String.format("请求参数值绑定错误url=%s, exMsg=%s", url, e.getMessage());
        sendOnOffMessage(loggerMessage);

        String defaultMessage = "Method argument not valid exception";
        String paramMessage = e.getBindingResult().getAllErrors()
                .stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
                .findFirst().orElse(defaultMessage);
        ParamException pe = new ParamException(paramMessage);
        return new ResponseData<>(false, pe.getCode(), pe.getErrMsg());
    }

    @ExceptionHandler(MultipartException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseData handleMultipartException(HttpServletRequest request, MultipartException e) {
        String url = String.valueOf(request.getRequestURL());
        String message = String.format("文件上传出错url=%s, exMsg=%s", url, e.getMessage());
        log.warn(message);
        ParamException pe = new ParamException(e.getMessage());
        sendOnOffMessage(message);
        return new ResponseData<>(false, pe.getCode(), pe.getErrMsg());
    }

    /**
     * 可以设置开关的异常信息
     *
     * @param loggerMessage
     */
    public abstract void sendOnOffMessage(String loggerMessage);

    /**
     * 可以设置开关的异常信息和异常
     *
     * @param loggerMessage
     * @param e
     */
    public abstract void sendOnOffMessage(String loggerMessage, Throwable e);

    /**
     * 错误级别的日志打印和异常
     *
     * @param loggerMessage
     * @param e
     */
    public abstract void sendErrorMsg(String loggerMessage, Throwable e);

    /**
     * 错误级别的日志
     *
     * @param loggerMessage
     */
    public abstract void sendErrorMsg(String loggerMessage);

}